package com.ct.deadlockdetetor.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ct.deadlockdetetor.common.enums.ResultCode;
import com.ct.deadlockdetetor.common.exception.ServiceException;
import com.ct.deadlockdetetor.domain.allocation.ResourceAllocation;
import com.ct.deadlockdetetor.domain.allocation.dto.AllocationQueryDTO;
import com.ct.deadlockdetetor.domain.allocation.dto.RequestResourceDTO;
import com.ct.deadlockdetetor.domain.allocation.vo.AllocationVO;
import com.ct.deadlockdetetor.domain.deadLock.DeadLock;
import com.ct.deadlockdetetor.domain.resource.Resource;
import com.ct.deadlockdetetor.domain.wait.ProcessWait;
import com.ct.deadlockdetetor.mapper.DeadLockMapper;
import com.ct.deadlockdetetor.mapper.ProcessWaitMapper;
import com.ct.deadlockdetetor.mapper.ResourceAllocationMapper;
import com.ct.deadlockdetetor.mapper.ResourceMapper;
import com.ct.deadlockdetetor.service.ResourceAllocationService;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Service
@Slf4j
public class ResourceAllocationServiceImpl implements ResourceAllocationService {

    @Autowired
    private ResourceAllocationMapper resourceAllocationMapper;

    @Autowired
    private ResourceMapper resourceMapper;

    @Autowired
    private ProcessWaitMapper processWaitMapper;

    @Autowired
    private DeadLockMapper deadLockMapper;

    @Override
    public boolean requestResource(RequestResourceDTO requestResourceDTO) {
        if (allocateResource(requestResourceDTO)) {
            // 分配成功
            return true;
        }

        // 分配失败, 加入进程等待记录
        ProcessWait processWait = new ProcessWait();
        processWait.setResourceId(requestResourceDTO.getResourceId());
        processWait.setProcessId(requestResourceDTO.getProcessId());
        processWait.setRequestedInstances(requestResourceDTO.getAllocatedInstances());
        processWaitMapper.insert(processWait);

        // 进行死锁检测
        if (!detectDeadlock(requestResourceDTO.getProcessId())) {
            log.info("无死锁~");
            return true;
        }

        throw new ServiceException(ResultCode.DEAD_LOCK_EXISTS);
    }

    @Override
    public List<AllocationVO> list(AllocationQueryDTO allocationQueryDTO) {
        PageHelper.startPage(allocationQueryDTO.getPageNum(), allocationQueryDTO.getPageSize());
        return resourceAllocationMapper.selectAllocationList(allocationQueryDTO);
    }

    /**
     * 死锁检测方法
     * @param processId 开始检测的进程 id
     * @return false : 无死锁, true : 存在死锁
     *
     * 要是将每一个进程和资源点看成是顶点, 进程和资源的分配关系看成是边
     * 那么这就是一个图了
     * 那么这里检测时候有死锁的算法思路就是检查图是否成环, 要是成环说明进程
     * 获取资源就是相互等待的, 那么形成了死锁
     * 这里用 dfs 来遍历第一个顶点相关联的每一个顶点, 用 visited 来记录走
     * 过的顶点, 要是在下面的 dfs 中遍历到了, 就说明是成环了的, 再用 path
     * 记录走过的路, 打印环的基本信息, 为下面死锁的解除记录信息
     */
    private boolean detectDeadlock(int processId) {
        Set<Integer> visited = new HashSet<>();
        List<Integer> path = new ArrayList<>();
        log.info("开始死锁检测~");
        return dfsDetect(processId, visited, path);
    }

    private boolean dfsDetect(int processId, Set<Integer> visited, List<Integer> path) {

        // 深搜出口
        if (visited.contains(processId)) {

            // 环的起点
            int cycleStart = path.indexOf(processId);

            // 环 list
            List<Integer> cycle = path.subList(cycleStart, path.size());

            // 这里在末尾添加一个起点
            cycle.add(processId);

            // 获取资源id
            Set<Integer> involvedResources = extractResourcesFromCycle(cycle);

//            System.out.println("检测到死锁!");
//            System.out.print("发现死锁环路: ");

            String message = "";

            for (int i = 0; i < cycle.size(); i++) {
                message += "P" + cycle.get(i);
                if (i < cycle.size() - 1) message += " → ";
            }

            // 为每个涉及的资源创建死锁记录
            for (Integer resourceId : involvedResources) {
                DeadLock deadLockMessage = new DeadLock();
                deadLockMessage.setResourceId(resourceId);
                deadLockMessage.setDeadLockMessage(message);
                deadLockMapper.insert(deadLockMessage);
            }

            return true;
        }

        visited.add(processId);
        path.add(processId);

        // 既然这个进程是要加入等待队列的, 那我们就需要检测它加入等待队列之后
        // 是否会和别的进程形成等待环, 那么我们就需要找出和它使用同一个资源的
        // 的进程, 那么先查找有关这个进程的等待记录
        List<ProcessWait> waitList = processWaitMapper.selectByProcessId(processId);

        for (ProcessWait wait : waitList) {
            // 通过资源在资源分配类中找出和它有相同资源的进程
            List<Integer> processList = resourceAllocationMapper.selectByResourceId(wait.getResourceId());
            for (int curProcessId : processList) {
                if (curProcessId != processId) { // 避免自循环

                    // 存在就深搜
                    if (dfsDetect(curProcessId, visited, path)) {

                        // 存在死锁就返回 true
                        return true;
                    }
                }
            }
        }

        // 恢复现场
        visited.remove(processId);
        path.remove(path.size() - 1);

        // 没有死锁
        return false;
    }

    // 从死锁环路中提取涉及的资源id
    private Set<Integer> extractResourcesFromCycle(List<Integer> cycle) {
    Set<Integer> resources = new HashSet<>();

    // 通过进程等待关系查找涉及的资源
    for (Integer processId : cycle) {
        List<ProcessWait> waitList = processWaitMapper.selectByProcessId(processId);
        for (ProcessWait wait : waitList) {
            resources.add(wait.getResourceId());
        }
    }

    return resources;
}

    // 分实例给进程
    private boolean allocateResource(RequestResourceDTO requestResourceDTO) {
        Resource resource = resourceMapper.selectById(requestResourceDTO.getResourceId());
        if (resource == null) {
            throw new ServiceException(ResultCode.RESOURCE_NOT_EXISTS);
        }

        // 检查该资源是否已在死锁信息表中
        QueryWrapper<DeadLock> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(DeadLock::getResourceId, requestResourceDTO.getResourceId());
        DeadLock existingDeadlock = deadLockMapper.selectOne(queryWrapper);
        if (existingDeadlock != null) {
            // 如果该资源已涉及死锁，可根据业务需求处理
            // 例如：抛出异常或记录警告信息
            log.warn("资源 {} 已涉及死锁，死锁信息: {}",
                    requestResourceDTO.getResourceId(),
                    existingDeadlock.getDeadLockMessage());
            // 根据业务需求决定是否继续分配或抛出异常
            throw new ServiceException(ResultCode.DEAD_LOCK_EXISTS);
        }

        if (resource.getTotalInstances() < requestResourceDTO.getAllocatedInstances()) {
            throw new ServiceException(ResultCode.RESOURCE_NOT_ENOUGH);
        }


        if (resource.getAvailableInstances() >= requestResourceDTO.getAllocatedInstances()) {

            // 成功分配
            resource.setAvailableInstances(resource.getAvailableInstances() - requestResourceDTO.getAllocatedInstances());
            resourceMapper.updateById(resource);

            ResourceAllocation resourceAllocation = new ResourceAllocation();
            resourceAllocation.setResourceId(requestResourceDTO.getResourceId());
            resourceAllocation.setProcessId(requestResourceDTO.getProcessId());
            resourceAllocation.setAllocationInstances(requestResourceDTO.getAllocatedInstances());
            resourceAllocationMapper.insert(resourceAllocation);
            return true;
        } else {
            // 实例数量太少, 分配不成功
            return false;
        }
    }
}
